package com.wxy.favorites.service;

import cn.hutool.core.util.StrUtil;
import com.wxy.favorites.config.AppConfig;
import com.wxy.favorites.constant.ErrorConstants;
import com.wxy.favorites.constant.PublicConstants;
import com.wxy.favorites.core.ApiResponse;
import com.wxy.favorites.core.BizException;
import com.wxy.favorites.core.NoRollbackException;
import com.wxy.favorites.core.PageInfo;
import com.wxy.favorites.dao.CategoryRepository;
import com.wxy.favorites.dao.FavoritesRepository;
import com.wxy.favorites.entity.Category;
import com.wxy.favorites.entity.Favorites;
import com.wxy.favorites.entity.Password;
import com.wxy.favorites.security.ContextUtils;
import com.wxy.favorites.security.SecurityUser;
import com.wxy.favorites.util.AssertUtils;
import com.wxy.favorites.util.PinYinUtils;
import com.wxy.favorites.util.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.util.*;

/**
 * @Author wangxiaoyuan
 * @Date 2020/4/24 11:50
 * @Description
 **/
@Slf4j
@Service
@Transactional(noRollbackFor = NoRollbackException.class)
public class CategoryService {

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private CategoryRepository categoryRepository;

    @Autowired
    private FavoritesRepository favoritesRepository;

    @Autowired
    private FavoritesService favoritesService;

    @Autowired
    private PasswordService passwordService;

    public Category save(Category category) {
        return categoryRepository.save(category);
    }

    public void deleteById(Integer id) {
        categoryRepository.deleteById(id);
    }

    public Category findExist(String name, Integer userId) {
        return categoryRepository.findByNameAndUserIdAndIsSystem(name, userId, PublicConstants.NORMAL_CATEGORY_CODE);
    }

    public List<Category> findByUserId(Integer userId) {
        List<Sort.Order> orders = new ArrayList<>();
        orders.add(new Sort.Order(Sort.Direction.DESC, "sort"));
        orders.add(new Sort.Order(Sort.Direction.ASC, "id"));
        return categoryRepository.findByUserId(userId, Sort.by(orders));
    }

    public Category findDefaultCategory(Integer userId) {
        return categoryRepository.findByNameAndUserIdAndIsSystem(PublicConstants.DEFAULT_CATEGORY_NAME, userId, PublicConstants.SYSTEM_CATEGORY_CODE);
    }

    public PageInfo<Category> findPageByUserId(Integer userId, Integer pageNum, Integer pageSize) {
        List<Sort.Order> orders = new ArrayList<>();
        orders.add(new Sort.Order(Sort.Direction.DESC, "sort"));
        orders.add(new Sort.Order(Sort.Direction.ASC, "id"));
        Pageable pageable = PageRequest.of(pageNum - 1, pageSize, Sort.by(orders));
        Page<Category> page = categoryRepository.findByUserId(userId, pageable);
        return new PageInfo<>(page.getContent(), page.getTotalPages(), page.getTotalElements());
    }

    public List<Category> findCategories(Integer userId, String searchName) {
        String text = SqlUtils.trimAndEscape(searchName);
        Pageable pageable = PageRequest.of(0, appConfig.getCategorySearchLimit());
        // 构造自定义查询条件
        Specification<Category> queryCondition = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            predicateList.add(criteriaBuilder.equal(root.get("userId"), userId));
            if (StrUtil.isNotBlank(text)) {
                predicateList.add(criteriaBuilder.or(criteriaBuilder.like(root.get("name"), "%" + text + "%"), criteriaBuilder.like(root.get("pinyin"), "%" + text + "%"), criteriaBuilder.like(root.get("pinyinS"), "%" + text + "%")));
            }
            return criteriaBuilder.and(predicateList.toArray(new Predicate[0]));
        };
        return categoryRepository.findAll(queryCondition, pageable).getContent();
    }

    public PageInfo<Category> findPage(Integer userId, String name, Integer pageNum, Integer pageSize) {
        String text = SqlUtils.trimAndEscape(name);
        List<Sort.Order> orders = new ArrayList<>();
        orders.add(new Sort.Order(Sort.Direction.DESC, "sort"));
        orders.add(new Sort.Order(Sort.Direction.ASC, "id"));
        Pageable pageable = PageRequest.of(pageNum - 1, pageSize, Sort.by(orders));
        // 构造自定义查询条件
        Specification<Category> queryCondition = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            predicateList.add(criteriaBuilder.equal(root.get("userId"), userId));
            if (StrUtil.isNotBlank(text)) {
                predicateList.add(criteriaBuilder.or(criteriaBuilder.like(root.get("name"), "%" + text + "%"), criteriaBuilder.like(root.get("pinyin"), "%" + text + "%"), criteriaBuilder.like(root.get("pinyinS"), "%" + text + "%")));
            }
            return criteriaBuilder.and(predicateList.toArray(new Predicate[0]));
        };
        Page<Category> page = categoryRepository.findAll(queryCondition, pageable);
        return new PageInfo<>(page.getContent(), page.getTotalPages(), page.getTotalElements());
    }

    public Category update(Category category) {
        return categoryRepository.save(category);
    }

    public Category findLinkCategory(Integer userId) {
        String name = PublicConstants.LINK_CATEGORY_NAME;
        Category category = categoryRepository.findByNameAndUserIdAndIsSystem(name, userId, PublicConstants.SPECIAL_CATEGORY_CODE);
        if (category == null) {
            category = new Category().setIsSystem(PublicConstants.SPECIAL_CATEGORY_CODE).setName(name).setPinyin(PinYinUtils.toPinyin(name)).setPinyinS(name)
                    .setSort(PublicConstants.MAX_SORT_NUMBER).setUserId(userId);
            categoryRepository.save(category);
        }
        return category;
    }

    public Category findHtmlCategory(Integer userId) {
        String name = PublicConstants.HTML_CATEGORY_NAME;
        Category category = categoryRepository.findByNameAndUserIdAndIsSystem(name, userId, PublicConstants.SPECIAL_CATEGORY_CODE);
        if (category == null) {
            category = new Category().setIsSystem(PublicConstants.SPECIAL_CATEGORY_CODE).setName(name).setPinyin(PinYinUtils.toPinyin(name)).setPinyinS(name)
                    .setSort(PublicConstants.MAX_SORT_NUMBER).setUserId(userId);
            categoryRepository.save(category);
        }
        return category;
    }

    public Category findTemplateCategory(Integer userId) {
        String name = PublicConstants.TEMPLATE_CATEGORY_NAME;
        Category category = categoryRepository.findByNameAndUserIdAndIsSystem(name, userId, PublicConstants.SPECIAL_CATEGORY_CODE);
        if (category == null) {
            category = new Category().setIsSystem(PublicConstants.SPECIAL_CATEGORY_CODE).setName(name).setPinyin(PinYinUtils.toPinyin(name)).setPinyinS(name)
                    .setSort(PublicConstants.MAX_SORT_NUMBER).setUserId(userId);
            categoryRepository.save(category);
        }
        return category;
    }

    public Integer setTop(Integer id) {
        Category category = categoryRepository.findById(id).orElseThrow(() -> new BizException("分类不存在"));
        if (Objects.equals(category.getIsSystem(), PublicConstants.NORMAL_CATEGORY_CODE)) {
            Integer userId = ContextUtils.getCurrentUser().getId();
            Integer max = Optional.ofNullable(categoryRepository.findMaxSortByUserId(userId)).orElse(0);
            category.setSort(max < PublicConstants.MAX_SORT_NUMBER ? max + 1 : PublicConstants.MAX_SORT_NUMBER);
            categoryRepository.save(category);
        }
        return category.getSort();
    }

    public Category saveCategory(Category category) {
        SecurityUser user = ContextUtils.getCurrentUser();
        AssertUtils.isNull(findExist(category.getName(), user.getId()), "分类已存在");
        category.setUserId(user.getId());
        // 拼音
        category.setPinyin(PinYinUtils.toPinyin(category.getName()));
        // 拼音首字母
        category.setPinyinS(PinYinUtils.toPinyinS(category.getName()));
        return categoryRepository.save(category);
    }

    public Category updateCategory(Category category) {
        Category category1 = categoryRepository.findById(category.getId()).orElse(null);
        AssertUtils.notNull(category1, "分类不存在");
        if (Objects.equals(category1.getIsSystem(), PublicConstants.NORMAL_CATEGORY_CODE)) {
            SecurityUser user = ContextUtils.getCurrentUser();
            Category category2 = findExist(category.getName(), user.getId());
            AssertUtils.isTrue(category2 == null || Objects.equals(category2.getId(), category1.getId()), "分类已存在");
            category1.setName(category.getName());
            category1.setPinyin(PinYinUtils.toPinyin(category.getName()));
            category1.setPinyinS(PinYinUtils.toPinyinS(category.getName()));
        }
        category1.setBookmark(category.getBookmark());
        return categoryRepository.save(category1);
    }

    public Boolean existName(String name) {
        SecurityUser user = ContextUtils.getCurrentUser();
        Category category = findExist(name, user.getId());
        return category != null;
    }

    public Boolean updateCheck(Integer id, String name) {
        AssertUtils.notNull(id, "id不能为空");
        AssertUtils.isTrue(StrUtil.isNotBlank(name), "name不能为空");
        SecurityUser user = ContextUtils.getCurrentUser();
        Category category = findExist(name, user.getId());
        return category != null && !Objects.equals(category.getId(), id);
    }

    public Category findCategory(Integer categoryId) {
        Category category = categoryRepository.findById(categoryId).orElse(null);
        AssertUtils.notNull(category, "分类不存在");
        List<Favorites> favorites = favoritesService.findByCategoryId(categoryId);
        category.setFavorites(favorites);
        return category;
    }

    public Boolean delete(Integer id) {
        Category category = categoryRepository.findById(id).orElse(null);
        AssertUtils.notNull(category, "分类不存在");
        AssertUtils.isTrue(!PublicConstants.SYSTEM_CATEGORY_CODE.equals(category.getIsSystem()), ErrorConstants.SYSTEM_CATEGORY_NO_DELETE_MSG);
        categoryRepository.deleteById(id);
        // 收藏移动至回收站
        List<Favorites> favoritesList = favoritesService.findByCategoryId(id);
        Category defaultCategory = findDefaultCategory(ContextUtils.getCurrentUser().getId());
        favoritesList.forEach(favorites -> {
            favorites.setDeleteFlag(PublicConstants.DELETE_CODE);
            favorites.setDeleteTime(new Date());
            favorites.setCategoryId(defaultCategory.getId());
            favoritesRepository.save(favorites);
        });
        return true;
    }

    public Boolean moveDefault(Integer id) {
        Category category = categoryRepository.findById(id).orElse(null);
        AssertUtils.notNull(category, "分类不存在");
        AssertUtils.isTrue(!PublicConstants.SYSTEM_CATEGORY_CODE.equals(category.getIsSystem()), ErrorConstants.SYSTEM_CATEGORY_NO_DELETE_MSG);
        categoryRepository.deleteById(id);
        // 收藏移动至回收站
        List<Favorites> favoritesList = favoritesService.findByCategoryId(id);
        Category defaultCategory = findDefaultCategory(ContextUtils.getCurrentUser().getId());
        favoritesList.forEach(favorites -> {
            favorites.setCategoryId(defaultCategory.getId());
            favoritesRepository.save(favorites);
        });
        return true;
    }

    public Boolean clean(Integer id) {
        List<Favorites> favoritesList = favoritesService.findByCategoryId(id);
        favoritesList.forEach(favorites -> {
            favorites.setDeleteFlag(PublicConstants.DELETE_CODE);
            favorites.setDeleteTime(new Date());
            favoritesService.update(favorites);
        });
        return true;
    }

    public Boolean bookmark(Category category) {
        Category category1 = categoryRepository.findById(category.getId()).orElse(null);
        AssertUtils.notNull(category1, ErrorConstants.ILLEGAL_OPERATION_MSG);
        category1.setBookmark(category.getBookmark());
        categoryRepository.save(category1);
        return true;
    }

    public Category findById(Integer id) {
        Category category = categoryRepository.findById(id).orElse(null);
        AssertUtils.notNull(category, "分类不存在");
        return category;
    }

    public Category exportCategory(Integer id) {
        Category category = findById(id);
        List<Favorites> favoritesList = favoritesService.findByCategoryId(category.getId());
        favoritesList.forEach(f -> {
            Password password = passwordService.findByFavoritesId(f.getId());
            f.setPassword(password);
        });
        category.setFavorites(favoritesList);
        return category;
    }

    public Category query(Integer id) {
        Category category = findById(id);
        category.setCount(favoritesRepository.countByCategoryId(id));
        return category;
    }
}

